package dao

import (
	"errors"
	"fmt"
	"html"
	"html/template"
	"math"
	"strings"
	"time"
)

type PostDataReal struct {
	Id           int    `json:"id"`
	Author       int    `json:"author"`
	PostType     int    `json:"post_type"`
	Title        string `json:"title"`
	Content      string `json:"content"`
	ReadNum      int    `json:"read_num"`
	IsPrivate    uint   `json:"is_private"`
	IsShow       uint   `json:"is_show"`
	IsRemark     uint   `json:"is_remark"`
	IsTemp       uint   `json:"is_temp"`
	IsStar       uint   `json:"is_star"`
	AddTime      string `json:"add_time"`
	LastEditTime string `json:"last_edit_time"`
}

type PostData struct {
	PostDataReal
	PostTypeHtml template.HTML `json:"post_type_info"`
	TitleHtml    template.HTML `json:"title"`
	ContentHtml  template.HTML `json:"content"`
	FmtShowTime  template.HTML `json:"fmt_show_time"`
	Tags         []TagData     `json:"tags"`
}

func (p *PostDataReal) Info() (err error) {
	id := p.Id
	if id < 1 {
		return errors.New("ID错误")
	}

	sql := fmt.Sprintf("select `id`,`author`,`post_type`,`title`,`content`,`read_num`,`is_private`,`is_show`,`is_remark`,`is_temp`,`is_star`,`add_time`,`last_edit_time` from bg_posts where id=%d", id)
	if Db == nil {
		InitMysql()
	}

	rows, err := Db.Query(sql)
	if err != nil {
		LogError(err)
		return
	}
	defer rows.Close()
	if rows.Next() {
		rows.Scan(&p.Id, &p.Author, &p.PostType, &p.Title, &p.Content, &p.ReadNum, &p.IsPrivate, &p.IsShow, &p.IsRemark, &p.IsTemp, &p.IsStar, &p.AddTime, &p.LastEditTime)
	}

	return
}

func (p *PostDataReal) Add() (err error) {

	sql := fmt.Sprintf("insert into bg_posts set `author`=%d,`post_type`=%d,`title`='%s',`content`='%s',`is_private`=%d,`is_show`=%d,`is_remark`=%d,`is_temp`=%d,`is_star`=%d,`add_time`='%s',`last_edit_time`='%s'",
		p.Author, p.PostType, p.Title, p.Content, p.IsPrivate, p.IsShow, p.IsRemark, p.IsTemp, p.IsStar, p.AddTime, p.LastEditTime)

	Result, err := Db.Exec(sql)
	if err != nil {
		return
	}
	id, err := Result.LastInsertId()
	if err != nil {
		return
	}
	p.Id = int(id)
	return
}

func (p *PostDataReal) Update() (err error) {

	id := p.Id
	if id < 1 {
		return errors.New("ID错误")
	}

	p.LastEditTime = time.Now().Format(TimeLayout)
	sql := fmt.Sprintf("update bg_posts set `post_type`=%d,`title`='%s',`content`='%s',`is_private`=%d,`is_show`=%d,`is_remark`=%d,`is_temp`=%d,`is_star`=%d,`last_edit_time`='%s' where id=%d",
		p.PostType, p.Title, p.Content, p.IsPrivate, p.IsShow, p.IsRemark, p.IsTemp, p.IsStar, p.LastEditTime, id)

	res, err := Db.Exec(sql)
	if err != nil {
		return
	}
	if num, err := res.RowsAffected(); num == 0 || err != nil {
		return errors.New("update error")
	}

	return
}

func (p *PostDataReal) Delete() (err error) {

	return
}

/**
 * 获取文章列表
 * @param page 页号
 * @param limitNum 获取的数量
 * @param types 分类
 */
func GetPostList(page, limitNum, types int64, isByAdmin bool) (lists PostListData) {
	if limitNum <= 0 {
		return
	}
	lists.CurrentPage = int(page)
	lists.DataNum = 0
	lists.PageNum = 0
	lists.PageSize = int(limitNum)
	lists.Lists = make(map[int64]PostData)

	var sql_type = ""
	if types != 0 {
		ids := GetSonIds(int(types))
		if len(ids) < 1 {
			sql_type = fmt.Sprintf("post_type=%d and ", types)
		} else {
			var str []string
			for _, id := range ids {
				str = append(str, fmt.Sprintf("%d", id))
			}
			sql_type = fmt.Sprintf("post_type in (%s) and ", strings.Join(str, ","))
		}
	}

	sql_count := fmt.Sprintf("select count(*) from `bg_posts` where %s is_show=1 and is_private=0 and is_temp=0", sql_type)
	if Db == nil {
		InitMysql()
	}

	err1 := Db.QueryRow(sql_count).Scan(&lists.DataNum)
	if err1 != nil {
		fmt.Println(err1.Error())
		return
	}
	if lists.DataNum == 0 {
		return
	}
	lists.PageNum = int(math.Ceil(float64(lists.DataNum) / float64(limitNum)))

	sql := fmt.Sprintf("select `id`,`author`,`post_type`,`title`,`content`,`read_num`,`add_time`,`is_remark`,`is_star`,`last_edit_time`,`is_private`,`is_show`,`is_temp` from bg_posts where %s"+
		"is_show=1 and is_private=0 and is_temp=0 order by add_time desc limit %d,%d ", sql_type, (page-1)*limitNum, limitNum)

	rows2, err := Db.Query(sql)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer rows2.Close()

	var m int64 = 0
	for rows2.Next() {
		var d PostData
		d.IsPrivate = 0
		d.IsTemp = 0
		d.IsShow = 1

		var title, content string
		err := rows2.Scan(&d.Id, &d.Author, &d.PostType, &title, &content, &d.ReadNum, &d.AddTime, &d.IsRemark, &d.IsStar, &d.LastEditTime, &d.IsPrivate, &d.IsShow, &d.IsTemp)
		if err != nil {
			LogError(err)
			return
		}
		d.TitleHtml = template.HTML(html.UnescapeString(title))
		d.ContentHtml = template.HTML(html.UnescapeString(content))
		d.Tags = GetPostTags(d.Id)

		postTypeInfo := GetPostTypeInfo(d.PostType)
		d.PostTypeHtml = postTypeInfo.ToHtml(isByAdmin)
		d.FmtShowTime = template.HTML(d.AddTime[:16])

		//模板的rang就是一个坑(它会自动按key做asc排序)
		lists.Lists[m] = d
		m++
	}

	return
}

func GetPostInfo(id int64, isByAdmin bool) (info PostData, err error) {
	if Db == nil {
		InitMysql()
	}
	var title, content string
	sql := fmt.Sprintf("select `id`,`author`,`post_type`,`title`,`content`,`read_num`,`add_time`,`is_private`,`is_show`,`is_remark`,`is_temp`,`is_star`,`last_edit_time` from bg_posts where id=%d", id)
	err = Db.QueryRow(sql).Scan(&info.Id, &info.Author, &info.PostType, &title, &content, &info.ReadNum, &info.AddTime, &info.IsPrivate, &info.IsShow, &info.IsRemark, &info.IsTemp, &info.IsStar, &info.LastEditTime)
	if err != nil {
		return
	}
	info.TitleHtml = template.HTML(html.UnescapeString(title))
	info.ContentHtml = template.HTML(html.UnescapeString(content))
	info.FmtShowTime = template.HTML(info.AddTime[:16])

	//如果有多个查询就使用goroutine
	info.Tags = GetPostTags(info.Id)

	postTypeInfo := GetPostTypeInfo(info.PostType)
	info.PostTypeHtml = postTypeInfo.ToHtml(isByAdmin)

	return
}

/**
 * 获取最新文章列表
 * @param limitNum 获取的数量
 */
func GetNewPostList(limitNum int64) (lists []NewPostList) {

	sql := fmt.Sprintf("select id,title from bg_posts order by add_time desc limit %d ", limitNum)
	if Db == nil {
		InitMysql()
	}

	rows, err := Db.Query(sql)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer rows.Close()

	var li NewPostList
	for rows.Next() {

		err1 := rows.Scan(&li.Id, &li.Title)
		if err1 != nil {
			fmt.Println(err1.Error())
		}
		lists = append(lists, li)
	}

	return
}

/**
获取分类的文章数
*/
func GetPostNum(postType, isShow int) (num int) {
	var whereString []string
	if isShow == 1 {
		whereString = append(whereString, " `is_show`=1 ")
	} else if isShow == 0 {
		whereString = append(whereString, " `is_show`=0 ")
	}

	sonIds := GetSonIds(postType)
	if len(sonIds) >= 1 {
		str := []string{fmt.Sprintf("%d", postType)}
		for _, v := range sonIds {
			str = append(str, string(fmt.Sprintf("%d", v)))
		}
		whereString = append(whereString, " `post_type` in ("+strings.Join(str, ",")+")")
	} else {
		whereString = append(whereString, " `post_type`="+fmt.Sprintf("%d", postType)+" ")
	}

	sql := fmt.Sprintf("select count(*) as num from bg_posts where %s ", strings.Join(whereString, " AND "))
	if Db == nil {
		InitMysql()
	}

	err := Db.QueryRow(sql).Scan(&num)
	if err != nil {
		fmt.Println(err)
		return
	}

	return
}

/**
增加文章的阅读数
*/
func IncPostReadNum(id int64) (err error) {
	if Db == nil {
		InitMysql()
	}
	_, err = Db.Exec("update bg_posts set read_num=read_num+1 where id=?", id)
	if err != nil {
		LogError(err)
		return
	}
	return nil
}

/**
根据关键字获取文章列表
*/
func GetPostListByKeyWord(keyWord, tagName string, typeId, page, limitNum int64, byAdmin bool) (lists PostListData) {
	if limitNum <= 0 {
		return
	}
	lists.CurrentPage = int(page)
	lists.DataNum = 0
	lists.PageNum = 0
	lists.PageSize = int(limitNum)
	lists.Lists = make(map[int64]PostData)

	var sql_where_arr = []string{}
	if typeId != 0 {
		ids := GetSonIds(int(typeId))
		if len(ids) < 1 {
			sql_where_arr = append(sql_where_arr, fmt.Sprintf("(post_type=%d)", typeId))
		} else {
			var str []string
			for _, id := range ids {
				str = append(str, fmt.Sprintf("%d", id))
			}
			sql_where_arr = append(sql_where_arr, fmt.Sprintf("(post_type in (%s))", strings.Join(str, ",")))
		}
	}

	if keyWord != "" {
		sql_where_arr = append(sql_where_arr, "(`title` like '%"+keyWord+"%' OR `content` like '%"+keyWord+"%')")
	}

	if tagName != "" {
		ids := GetPostIdsByTagName(tagName)
		if len(ids) < 1 {
			return
		} else {
			var str []string
			for _, id := range ids {
				str = append(str, fmt.Sprintf("%d", id))
			}
			sql_where_arr = append(sql_where_arr, fmt.Sprintf("(id in (%s))", strings.Join(str, ",")))
		}
	}

	//是否前台调用
	if !byAdmin {
		sql_where_arr = append(sql_where_arr, "(is_show=1 and is_private=0 and is_temp=0)")
	}
	sql_where_arr = append(sql_where_arr, "1")

	var sql_where = strings.Join(sql_where_arr, " AND ")

	sql_count := fmt.Sprintf("select count(*) from `bg_posts` where %s ", sql_where)

	if Db == nil {
		InitMysql()
	}

	err1 := Db.QueryRow(sql_count).Scan(&lists.DataNum)
	if err1 != nil {
		fmt.Println(err1.Error())
		return
	}
	if lists.DataNum == 0 {
		return
	}

	lists.PageNum = int(math.Ceil(float64(lists.DataNum) / float64(limitNum)))

	sql := fmt.Sprintf("select `id`,`author`,`post_type`,`title`,`content`,`read_num`,`add_time`,`is_remark`,`is_star`,`last_edit_time`,`is_private`,`is_show`,`is_temp` from bg_posts where %s"+
		" order by add_time desc,id desc limit %d,%d ", sql_where, (page-1)*limitNum, limitNum)
	rows2, err := Db.Query(sql)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer rows2.Close()

	var m int64 = 0
	for rows2.Next() {
		var d PostData
		d.IsPrivate = 0
		d.IsTemp = 0
		d.IsShow = 1

		var title, content string
		err := rows2.Scan(&d.Id, &d.Author, &d.PostType, &title, &content, &d.ReadNum, &d.AddTime, &d.IsRemark, &d.IsStar, &d.LastEditTime, &d.IsPrivate, &d.IsShow, &d.IsTemp)
		if err != nil {
			LogError(err)
			return
		}
		d.TitleHtml = template.HTML(html.UnescapeString(title))
		d.ContentHtml = template.HTML(html.UnescapeString(content))
		d.Tags = GetPostTags(d.Id)

		postTypeInfo := GetPostTypeInfo(d.PostType)
		d.PostTypeHtml = postTypeInfo.ToHtml(byAdmin)
		d.FmtShowTime = template.HTML(d.AddTime[:16])
		if d.LastEditTime == "1970-01-01 00:00:00" {
			d.LastEditTime = ""
		}
		//模板的rang就是一个坑(它会自动按key做asc排序)
		lists.Lists[m] = d
		m++
	}

	return
}

func GetAllPost(isByAdmin bool) (lists []PostData, err error) {
	sql := fmt.Sprintf("select `id`,`author`,`post_type`,`title`,`content`,`read_num`,`is_private`,`is_show`,`is_remark`,`is_temp`,`is_star`,`add_time`,`last_edit_time` from bg_posts where 1")
	rows2, err := Db.Query(sql)
	if err != nil {
		return
	}
	defer rows2.Close()

	for rows2.Next() {
		var d PostData
		var title, content string
		err := rows2.Scan(&d.Id, &d.Author, &d.PostType, &title, &content, &d.ReadNum, &d.IsPrivate, &d.IsShow, &d.IsRemark, &d.IsTemp, &d.IsStar, &d.AddTime, &d.LastEditTime)
		if err != nil {
			continue
		}
		d.TitleHtml = template.HTML(html.UnescapeString(title))
		d.ContentHtml = template.HTML(html.UnescapeString(content))
		d.Tags = GetPostTags(d.Id)
		postTypeInfo := GetPostTypeInfo(d.PostType)
		d.PostTypeHtml = postTypeInfo.ToHtml(isByAdmin)
		d.FmtShowTime = template.HTML(d.AddTime[:16])
		lists = append(lists, d)
	}

	return
}

/**
检查文章标题是否合法
*/
func CheckPostTitle(title string) (isOk bool, err string) {

	if len(title) < 4 {
		return false, "标题文字不得少于4个字符"
	}

	if len(title) > 24 {
		return false, "标题文字不得多于24个字符"
	}

	isOk = true

	return
}

/**
检查文章内容是否合法
*/
func CheckPostContent(content string) (isOk bool, err string) {

	if len(content) < 6 {
		return false, "内容文字不得少于6个字符"
	}

	if len(content) > 1024*1024 {
		return false, "内容太长"
	}

	isOk = true

	return
}
